package com.ewancle;

import com.alibaba.fastjson2.JSON;
import com.ewancle.model.Subject;
import com.ewancle.utils.PostgreSqlJdbc;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.sql.SQLException;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

public class SeleniumTestQuestion {

    private static final String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
    private static final WebDriver driver;

    private static Map<String, String> cookieMap = null;
    private static final Map<String, String> headers = new HashMap<>();

    private static String cookiesStatic = "";

    static {

        //headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 10; Mi 9T Pro Build/QKQ1.190910.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 MicroMessenger/8.0.17.2010(0x2800113B) NetType/WIFI Language/zh_CN");
        headers.put("User-Agent", userAgent);
        headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        //headers.put("Accept-Encoding", "gzip, deflate, br, zstd");
        headers.put("Accept-Encoding", "gzip, deflate, br");

        headers.put("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
        headers.put("Connection", "keep-alive");
        //headers.put("Referer", referer); // 如果需要Referer信息
        headers.put("Upgrade-Insecure-Requests", "1");
        headers.put("Cache-Control", "max-age=0");
        //headers.put("content-type", "application/x-www-form-urlencoded; charset=UTF-8");


        String os = System.getProperty("os.name");
        //Windows操作系统
        if (os != null && os.toLowerCase().startsWith("windows")) {
            System.out.println(String.format("当前系统版本是:%s", os));
            System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver.exe");
        } else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
            System.out.println(String.format("当前系统版本是:%s", os));
            System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver");
        } else { //其它操作系统
            System.out.println(String.format("当前系统版本是:%s", os));
        }

        ChromeOptions options = new ChromeOptions();

        // 设置其它 Chrome 配置选项
        options.addArguments("--disable-blink-features=AutomationControlled"); // 禁用自动化特性,在某些网站上，Selenium 可能被检测为自动化工具，可以通过禁用 webdriver 特性来绕过检测。
        options.addArguments("--disable-extensions");  // 禁用扩展

        options.addArguments("--disable-gpu");  // 禁用 GPU
        options.addArguments("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
        options.addArguments("Accept-Language", "en-US,en;q=0.9");
        //options.addArguments("accept-encoding", "gzip, deflate, br, zstd");
        options.addArguments("Connection", "keep-alive");
        //options.addArguments("Upgrade-Insecure-Requests", "1");
        options.setExperimentalOption("useAutomationExtension", false); // 通过 setExperimentalOption 启用一些实验性的 Chrome 功能
        // 设置 User-Agent 和其他请求头，模拟真实的安卓设备浏览器
        options.addArguments("user-agent="+userAgent);

        // 创建 WebDriver 实例
        driver = new ChromeDriver(options);
        //修改 WebDriver 属性: 某些网站会通过 navigator.webdriver 判断是否是自动化浏览器。可以通过 JavaScript 设置这些属性来避免被识别为 Selenium
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");

        // 使用 DevTools 获取网络请求的 headers (版本要对上）
        /*DevTools devTools = ((ChromeDriver) driver).getDevTools();
        devTools.createSession();
        devTools.addListener(Network.requestWillBeSent(), (RequestWillBeSent request) -> {
            System.out.println("Request Headers: " + request.getRequest().getHeaders());
        });

        devTools.addListener(Network.responseReceived(), (ResponseReceived response) -> {
            System.out.println("Response Headers: " + response.getResponse().getHeaders());
        });*/
    }

    public static void main(String[] args) {
        try {
            // 系统登陆
            login();
            // 等待滑动验证
            waitLoadStatus(1, 10, null, null,0);
            // 查找并点击 Layui 弹窗的关闭按钮 (登录完拖动验证码后，关闭弹窗）
            driver.findElement(By.xpath("//*[@id=\"layui-layer2\"]/div[3]/a")).click();
            waitLoadStatus(0, 0, null, null,0);

            // 模拟 点击题库菜单 --> 点击 vip题库
            // 1 移动鼠标到
            By tikuEl = By.xpath("/html/body/div[2]/div[1]/div/div[1]/div/ul/li[4]/a");
            waitLoadStatus(4, 20, tikuEl, null,0);
            moveClick(tikuEl, false);

            // 获取当前窗口的唯一信息
            String topWindowHandle = driver.getWindowHandle();

            // 点击 vip题库
            By vipTikuEl = By.xpath("/html/body/div[2]/div[1]/div/div[1]/div/ul/li[4]/ul/li[1]");
            waitLoadStatus(3, 20, vipTikuEl, null,0);
            moveClick(vipTikuEl, true);

            // 切换到下一个页面
            nextWindow(Set.of(topWindowHandle));

            String top2WindowHandle = driver.getWindowHandle();
            // 点击新窗口的 【在线做题】
            By zaixianZuotiEl = By.xpath("/html/body/div[3]/div[1]/div[1]/a/span");
            waitLoadStatus(3, 20, zaixianZuotiEl, null,0);
            driver.findElement(zaixianZuotiEl).click();
            waitLoadStatus(2, 5, null, null,0);
            // 获取当前窗口的唯一信息
            //switchWindow("阿虎医考（编题16年，更懂必考点）");
            // 切换到下一个页面
            nextWindow(Set.of(topWindowHandle, top2WindowHandle));


            //System.out.println("当前网页源代码"+ driver.getPageSource());
            // 模拟下拉动作
            By xialaEl = By.xpath("/html/body/div[3]/div/a/img");
            waitLoadStatus(3, 20, xialaEl, null,0);
            moveClick(xialaEl, true);

            // 主菜单
            String mainMenuXpath="//ul[@id=\"pills-tab\"]/li/a[@href = '#v-pills-home0']";
            By mainMenuEl = By.xpath(mainMenuXpath);
            waitLoadStatus(3, 20, mainMenuEl, null,0);
            moveClick(mainMenuEl, true);

            // 选择子分类菜单
            // //*[@id="v-pills-home25"]/div/div[16]/div[1]/p
            String menuButtonPath="//div[@id='v-pills-home0']/div/div/div[1]/p[@data='110']";
            By mainMenuSubEl = By.xpath(menuButtonPath);
            waitLoadStatus(3, 20, mainMenuSubEl, null,0);
            moveClick(mainMenuSubEl, true);

            // 确认按钮
            driver.findElement(By.xpath("/html/body/div[3]/div[3]/div[2]/button")).click();

            // 选择刷题按钮
            String shuatiXpath="/html/body/div[4]/div[3]/div[2]/div[3]/div[2]";
            By shuatiEl = By.xpath(shuatiXpath);
            waitLoadStatus(3, 20, shuatiEl, null,0);
            moveClick(shuatiEl, true);

            // 选择科目
            String kemu1Xpath="/html/body/div[4]/a[9]";
            By kemu1El = By.xpath(kemu1Xpath);
            waitLoadStatus(3, 20, kemu1El, null,0);
            moveClick(kemu1El, true);
            String kemu2Xpath="/html/body/div[4]/div[2]/div[1]";
            By kemu2El = By.xpath(kemu2Xpath);
            waitLoadStatus(3, 20, kemu2El, null,0);
            moveClick(kemu2El, true);


            // 获取cookies
            cookieMap = cookiesBySelenium();

            // 查询要爬取的子科目信息
            String sql = "SELECT id, \"name\", parent_id AS parentId, href, onclick, part_id partId FROM t_suject WHERE parent_id IN (SELECT suject_id FROM t_topic_suject WHERE topic_id IN (SELECT id FROM t_topic WHERE status = 2))";
            try {
                List<Subject> data = PostgreSqlJdbc.queryPaquSubject(List.of(), sql);
                //System.out.println(JSON.toJSONString(data));
                for(Subject dataItem : data){
                    // 获取科目下全部的题目信息
                    System.out.println(JSON.toJSONString(dataItem));
                    getQuestionData(dataItem);
                    if(true){
                        return;
                    }
                }

            } catch (SQLException e) {
                throw new RuntimeException(e);
            }

        }finally {
            // 关闭
            close();
        }
    }

    /**
     * 获取题库信息
     */
    public static void getQuestionData(Subject queryObj){
        int sumNum = 0;

        Long lastDataId = 0L;
        int readCount = 0;
        Set<Long> dataIdSet = new HashSet<>(); // 全部的dataid

        do {
            // 每页的数据保存
            List<

            if(sumNum!=0 && readCount>=sumNum){
                break;
            }
            String url ;
            if(lastDataId == 0){
                // 直接请求数据 /ahutiku/PrefQue/getQueList.html?part_id=" + part_id + "&dir_type=0&next_page=2
                url = String.format("https://waph.ahuyk.com/ahutiku/PrefQue/getQueList/part_id/%d.html", queryObj.getPartId());
            }else {
                url = "https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id="+queryObj.getPartId()+
                        "&dir_type=0&next_page=2&last_group_id="+lastDataId+"&wap_group_num="+readCount;
            }

            // 测试使用，配合结尾的break；
            // 共用题干的题目
            //url="https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id=6227&dir_type=0&next_page=2&last_group_id=346527&wap_group_num=114";
            // 共用选项的题目
            //url="https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id=6228&dir_type=0&next_page=2&last_group_id=346727&wap_group_num=184";

            Document document = jsoupSend(url, false, null, null);
            //System.out.println("发送请求的网页："+document.toString());
            /*if(!document.is("body > div.wap_content > div.page_box > div.swiper-container > div.swiper-wrapper > div.swiper-slide")){
                System.out.println("不存在元素");
                break;
            }*/

            //System.out.println(document);
            // 获取科目下的题目总数据量(第一次时候获取总数)
            if(lastDataId == 0) {
                sumNum = Integer.parseInt(document.select("body > div.wap_content > div.part_num_box > div > div.part_num_boxs > span#group_num").text());
                System.out.println("该分类下的题目总数：" + sumNum);
            }

            // 获取每页题目列表信息
            Elements es = document.select("body > div.wap_content > div.page_box > div.swiper-container > div.swiper-wrapper > div.swiper-slide");
            if(es.isEmpty()){
                break;
            }
            for (Element questionEt : es) {
                //System.out.println(questionEt.toString());
                // 题型的结构类型：1:普通单选 2:共用题干 3:共用选项  4:普通多选题
                int questionType = Integer.parseInt(questionEt.attr("data-que-type"));
                // 对于2 公用题干题型，题干内容在 data-pub-que-text
                String question2Content = questionEt.attr("data-pub-que-text");
                // 对于3 公用选项题型，题干内容在 data-pub-que-text
                String question3Content = questionEt.select("div > div.que_content > div.b_xt_box > div.que_opt_box > span.que_type").text();
                if(question3Content.isEmpty()){
                    question3Content = questionEt.attr("data-type-name");
                }

                // 当前页面题的位置索引
                String pageIndex = questionEt.attr("aria-label");
                // 课题科目的partId
                long partId = Long.parseLong(questionEt.attr("data-part-id"));
                Long dataId = Long.valueOf(questionEt.attr("data-id"));
                if(dataIdSet.contains(dataId)){ // 如果处理过，跳过本次题目处理
                    continue;
                }
                // 问题内容（题干）
                String questionContent = questionEt.select("div > div.que_content > div.que_text_box").text();
                // 正确答案
                String successAnswer = questionEt.select("div > div.que_content > div.que_opt_box > div.que_opt_item.success_answer > div.que_opt_option").stream().map(Element::text).collect(Collectors.joining("#"));

                // 获取解析(可以把页面定位到选题页面，通过Selenium调用js反解析加密内容
                // https://waph.ahuyk.com/ahutiku/PrefQue/getExplain.html POST Form data: group_id: 346442
                // jsoupSend("https://waph.ahuyk.com/ahutiku/PrefQue/getQueList/part_id/9406.html", true, Map.of("group_id", dataId.toString()), null);
                /*String jiexi = httpSend("https://waph.ahuyk.com/ahutiku/PrefQue/getExplain.html", "group_id=" + dataId);
                String explainRet = JSON.parseObject(jiexi).getJSONObject("data").getString("explain");
                System.out.println("解析内容=" + explainRet);
                JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
                // 定义要执行的 JavaScript 代码
                String script = "return decrypt(arguments[0], arguments[1], arguments[2]);";
                // 执行 JavaScript 代码并获取返回值
                Object explain = jsExecutor.executeScript(script, explainRet, "b3eb4a5426e15ed6", "426e15ed668d82b5");
                System.out.println(explain.toString());
                try {
                    Thread.sleep(500 + (long) (Math.random() * 2000));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
                // 官方解析，直接可提取，不需要调用接口
                String questExplain = questionEt.select("div > div.que_content > div.que_plan_box").toString();
                questExplain = questExplain.replaceAll("<!--","");
                questExplain = questExplain.replaceAll("-->","");
                Document guanfanjiexiDoc = Jsoup.parse(questExplain);
                String guanfanText = guanfanjiexiDoc.select("div > div > div > div:nth-child(2)").text();
                System.out.println("官方解析："+guanfanText);

                // 选项
                if(questionType == 1 || questionType == 4){
                    System.out.println("题目类型：" + questionType + "；题在页面的索引：" + pageIndex + "; 科目id" + partId + "; 题目：" + questionContent + ";正确答案：" + successAnswer);

                    Elements optEs = questionEt.select("div > div.que_content > div.que_opt_box > div.que_opt_item");
                    for (Element optEt : optEs) {
                        // 选项编码
                        String optCode = optEt.select("div > div.que_opt_option").text();
                        // 选项内容
                        String optContent = optEt.select("div > div.que_opt_con").text();
                        System.out.println("题目选项：" + optCode + "；题目选项内容：" + optContent);
                    }
                }else if(questionType == 2){ //https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id=6227&dir_type=0&next_page=2&last_group_id=346527&wap_group_num=114
                    System.out.println("题目类型：" + questionType + "；题在页面的索引：" + pageIndex + "; 科目id" + partId + "; 题目：" + question2Content + ";  正确答案：因为是共用题干，答案在下面子题");

                    Elements optEs = questionEt.select("div > div.que_content > div.b_xt_box > div[class='']");
                    for (Element optEt : optEs) {
                        // 子题干
                        String subQ  = optEt.select("div > div.que_text_box").text();
                        // 子题干选项内容
                        Elements subOptEs = optEt.select("div > div.que_opt_box > div.que_opt_item");
                        // 共用题干的选项
                        String subOptEsAnswer = optEt.select("div > div.que_opt_box > div.que_opt_item.success_answer > div.que_opt_option").stream().map(Element::text).collect(Collectors.joining("#"));
                        System.out.println("子题： 子题干="+subQ + "  ；正确答案="+subOptEsAnswer);
                        for(Element subOptItem : subOptEs){
                            String subOpVal = subOptItem.select("div > div.que_opt_option").text();
                            String subOpContent = subOptItem.select("div > div.que_opt_con").text();
                            System.out.println("子题： 子题选项="+subOpVal + " ；选项内容="+subOpContent);
                        }
                    }
                }else if (questionType == 3){ // https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id=6228&dir_type=0&next_page=2&last_group_id=346727&wap_group_num=184
                    System.out.println("题目类型：" + questionType + "；题在页面的索引：" + pageIndex + "; 科目id" + partId + "; 题目：" + question3Content + ";  正确答案：因为共用选项，所以选项固定，但是多个子问题单选共用的共用的某答案项目");
                    //#que_items_22 > div.que_content > div.b_xt_box > div > div.que_opt_box > div.que_opt_item
                    // 获取选项
                    Elements optEs = questionEt.select("div > div.que_content > div.b_xt_box > div > div.que_opt_box > div.que_opt_item");
                    for (Element optEt : optEs) {
                        String subOpVal = optEt.select("div > div:nth-child(1)").text();
                        String subOpContent = optEt.select("div > div.que_opt_con").text();
                        System.out.println("共用选项："+subOpVal + " 选项内容："+subOpContent);
                    }
                    // 查找选项被哪些题共用了
                    //#que_items_22 > div.que_content > div.b_xt_box > div:nth-child(2)
                    Elements commEs = questionEt.select("div > div.que_content > div.b_xt_box > div.que_text_box");
                    Elements commOpyEs = questionEt.select("div > div.que_content > div.b_xt_box > ul.b_da_an_box");
                    int nthChildIndex = 0;
                    for(Element commEt :  commEs){
                        String ziTiComm = commEt.text();
                        /*Elements commOptsEs = questionEt.select("div > div.que_content > div.b_xt_box > ul.b_da_an_box:nth-child("+(nthChildIndex*2+1)+") > li.an_item");
                        // 获取正确答案
                        String aw = questionEt.select("div > div.que_content > div.b_xt_box > ul.b_da_an_box:nth-child("+(nthChildIndex*2+1)+") > li.an_item.success_answer > div.que_opt_option").stream().map(Element::text).collect(Collectors.joining("#"));
                        List<String> lst = new ArrayList<>();
                        for(Element commOptsEt : commOptsEs){
                            String xuanXiangZimu = commOptsEt.select("li > div.que_opt_option").text();
                            lst.add(xuanXiangZimu);
                        }*/
                        Element commOpItemEs = commOpyEs.get(nthChildIndex);
                        Elements commOptsEs =  commOpItemEs.select("ul > > li.an_item");
                        // 获取正确答案
                        String aw = commOpItemEs.select("ul > li.an_item.success_answer > div.que_opt_option").stream().map(Element::text).collect(Collectors.joining("#"));
                        List<String> lst = new ArrayList<>();
                        for(Element commOptsEt : commOptsEs){
                            String xuanXiangZimu = commOptsEt.select("li > div.que_opt_option").text();
                            lst.add(xuanXiangZimu);
                        }
                        System.out.println("共用选项的项目："+ziTiComm + "    选项内容："
                                +lst.stream().collect(Collectors.joining("#")) + "    答案 ： "+aw);
                        nthChildIndex++;
                    }
                }

                readCount++;
                lastDataId = dataId;
                dataIdSet.add(dataId);
                System.out.println("该分类下的已经解析到：" + readCount + "题");
            }

            // 测试使用
            /*if(true){
                break;
            }*/
        } while (true);
        //System.out.println(document.html());
        // 下一页
        // https://waph.ahuyk.com/ahutiku/PrefQue/getQueList.html?part_id=6227&dir_type=0&next_page=2&last_group_id=346443&wap_group_num=30
        // /ahutiku/PrefQue/getQueList.html?part_id=" + part_id + '&dir_type=0&next_page=2&last_group_id=' + lastGroupId + '&wap_group_num=' + swiperIndexNum
        //boolean readNext = true;
        /*do{
            *//*if(readCount >= sumNum){
                break;
            }*//*
            Document documentLoop = jsoupSend("https://waph.ahuyk.com/ahutiku/PrefQue/getQueList/part_id/9406.html", false, null, null);
            if(Objects.isNull(documentLoop)){
                break;
            }

        }while (true);*/
    }

    /**
     * 模拟登陆
     */
    public static void login(){
        // 等待js加载完成
        waitLoadStatus(0, 0, null, null,0);
        driver.get("https://www.ahuyikao.com/login/passwordlogin.html");
        WebElement el_name = driver.findElement(By.id("phones"));
        el_name.sendKeys("13337839087");
        WebElement el_pwd = driver.findElement(By.id("passwords"));
        el_pwd.sendKeys("yunsheng6088");
        WebElement button = driver.findElement(By.xpath("//*[@id=\"login_password_form\"]/div[3]/button"));
        button.click();
    }

    /**
     * 刷新页面
     */
    public static void refreshPage(){
        driver.navigate().refresh();
        waitLoadStatus(0, 0, null, null,0);
        driver.navigate().back();
        waitLoadStatus(2, 1, null, null,0);
        driver.navigate().forward();
        waitLoadStatus(2, 1, null, null,0);
        driver.navigate().refresh();
        waitLoadStatus(0, 0, null, null,0);
    }

    // https://juejin.cn/post/7229625586150735928
    public static String httpSend(String url, String formData) {
        try{
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).timeout(Duration.ofMinutes(2));
            for(Map.Entry<String, String> mItem : headers.entrySet()){
                if(mItem.getKey().equals("Connection")){
                    continue;
                }
                builder = builder.setHeader(mItem.getKey(), mItem.getValue());
            }
            builder = builder.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
            // https://stackoverflow.com/questions/69837157/setting-a-session-cookie-with-the-java-11-httpclient 和 https://cloud.tencent.com/developer/article/2171091
            builder = builder.setHeader("cookie", cookiesStatic);
            // formData = "id=1&userName=赵云"
            HttpRequest request = builder.POST(HttpRequest.BodyPublishers.ofString(formData)).build();
            return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 发送Jsoup请求
     * @param url
     * @param isPost
     * @param formData
     * @param jsonData
     * @return
     */
    public static Document jsoupSend(String url, boolean isPost, Map<String,String> formData, String jsonData){
        try {
            if(!isPost){
                return Jsoup.connect(url)
                        .headers(headers)  // 设置请求头
                        .cookies(cookieMap)
                        .timeout(10000)     // 设置超时时间
                        .get();
            }else {
                if(Objects.nonNull(formData) && !formData.isEmpty()){
                    return Jsoup.connect(url)
                            .headers(headers)  // 设置请求头
                            .cookies(cookieMap)
                            .timeout(10000)     // 设置超时时间
                            .data(formData)
                            .post();
                }else if (Objects.nonNull(jsonData) && !jsonData.isEmpty()){
                    headers.put("Content-Type", "application/json");
                    return Jsoup.connect(url)
                            .headers(headers)  // 设置请求头
                            .cookies(cookieMap)
                            .timeout(10000)     // 设置超时时间
                            .requestBody(jsonData)
                            .post();
                }else {
                    return Jsoup.connect(url)
                            .headers(headers)  // 设置请求头
                            .cookies(cookieMap)
                            .timeout(10000)     // 设置超时时间
                            .post();
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从Selenium获取cookies
     * @return
     */
    public static Map<String, String> cookiesBySelenium(){
        // 获取所有的 cookie 信息
        Set<Cookie> cookies = driver.manage().getCookies();

        Map<String, String> cookieMap = new HashMap<>();
        // 获取每个 cookie 的信息
        for (Cookie cookie : cookies) {
            cookieMap.put(cookie.getName(), cookie.getValue());
        }

        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
        cookiesStatic = (String) jsExecutor.executeScript("return document.cookie;");

        /*System.out.println(JSON.toJSONString(cookieMap));

        // 执行 JavaScript 获取 cookies
        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
        String cookies = (String) jsExecutor.executeScript("return document.cookie;");
        // 输出获取到的 cookies
        System.out.println("Cookies: " + cookies);

        // 执行 JavaScript 获取 cookies，并将其解析为 Map
        JavascriptExecutor jsExecutorMap = (JavascriptExecutor) driver;
        String script = "var cookies = document.cookie.split(';');" +
                "var cookieMap = {};" +
                "cookies.forEach(function(cookie) {" +
                "    var parts = cookie.split('=');" +
                "    cookieMap[parts[0].trim()] = parts[1] ? parts[1].trim() : '';" +
                "});" +
                "return cookieMap;";

        // 执行 JavaScript
        Map<String, String> cookiesMap = (Map<String, String>) jsExecutorMap.executeScript(script);

        // 输出所有 cookies
        System.out.println("Cookies Map: " + cookiesMap);*/
        return cookieMap;
    }

    /**
     * 鼠标移动到元素，可执行单击
     * @param selector 选择的元素
     * @param onClick 是否需要单击：true表示移动到元素后并单击
     */
    public static void moveClick(By selector, boolean onClick){
        Actions actions = new Actions(driver);
        WebElement element = driver.findElement(selector);
        if(onClick){
            actions.moveToElement(element).click().perform();
        }else {
            actions.moveToElement(element).perform();
        }
    }

    public static void nextWindow(Set<String> currentWindowHandle){
        Set<String> windowHandlesTop = driver.getWindowHandles();
        for (String handle : windowHandlesTop) {
            driver.switchTo().window(handle);  // 切换到新窗口
            if (!currentWindowHandle.contains(handle)){
                break;
            }
        }
    }

    /**
     * 切换多窗口到自己想要的title页面
     * @param title
     */
    public static void switchWindow(String title){
        Set<String> windowHandlesTop = driver.getWindowHandles();
        for (String handle : windowHandlesTop) {
            System.out.println("窗口Title"+driver.getTitle());
            driver.switchTo().window(handle);  // 切换到新窗口
            System.out.println("切换窗口Title"+driver.getTitle());
            if (title.equals(driver.getTitle())){
                break;
            }
        }
    }

    /**
     * 关闭窗体
     */
    public static void close(){

        Set<String> windowHandles = driver.getWindowHandles();
        int sumWindow = windowHandles.size();
        int closeIndex = 0;
        // 6. 切换到新打开的窗口
        for (String handle : windowHandles) {
            // 如果调用 driver.close() 的是最后一个或唯一一个窗口，Selenium 会终止会话。
            if(closeIndex >= (sumWindow-1)){
                break;
            }
            driver.switchTo().window(handle);
            // 获取关闭之前的窗口句柄数
            Set<String> originalHandles = driver.getWindowHandles();
            driver.close();
            // 使用 WebDriverWait 等待窗口句柄数量减少
            new WebDriverWait(driver, Duration.ofSeconds(10)).until((ExpectedCondition<Boolean>) d ->
                    {
                        assert d != null;
                        return d.getWindowHandles().size() < originalHandles.size();
                    }
            );
            closeIndex++;
        }
        // 使用 WebDriverWait 等待窗口句柄数量减少
        /*new WebDriverWait(driver, Duration.ofSeconds(10)).until((ExpectedCondition<Boolean>) d ->
                d.getWindowHandles().size() < windowHandles.size()
        );*/
        System.out.println("窗口已完全关闭");
        driver.quit();
    }



    /**
     * 等待页面加载
     * @param waitType 等待类型 ：
     *                 1 固定线程等待;
     *                 2 在默认等待基础上随机1～4秒等待；
     *                 3 等待元素变为可点击
     *                 4 等待元素变为可见。
     *                 5 等待元素出现在 DOM 中，但不一定可见。
     *                 6 等待某个元素包含指定的文本。
     *                 7 等待窗口数量达到某个数量
     *                 其他 等待页面js初始完成
     * @param waitTime 等待时间（单位秒）
     * @param selector 对于元素选择器的元素等待
     * @param elText 对于类型6需要指定
     * @param windowsCountNum 对于类型7需要指定
     * @return 如果指定元素选择器等待完毕返回该元素
     */
    private static boolean waitLoadStatus(int waitType, long waitTime, By selector, String elText, int windowsCountNum){
        WebDriverWait wait = null;
        if(waitType > 0  && waitType < 10 && waitTime <= 0 ){
            throw new RuntimeException("请设定等待时间");
        }else if(waitType == 7 && windowsCountNum <= 1 ){
            throw new RuntimeException("请设定等待多少窗口出现");
        }else if (waitType > 2  && waitType < 8 && Objects.isNull(selector)){
            throw new RuntimeException("请设定等待的元素");
        }else if (waitType == 6 && (Objects.isNull(elText) || elText.trim().isEmpty())){
            throw new RuntimeException("请设定等待的元素出现的值");
        }

        if(waitTime > 0){
            wait = new WebDriverWait(driver, Duration.ofSeconds(waitTime));
        }
        boolean isOk = false;
        try {
            // 等待页面加载完成（直到没有任何正在加载的资源）
            isOk = switch (waitType) {
                case 1 -> {
                    Thread.sleep(waitTime * 1000);
                    yield true;
                }
                case 2 -> {
                    Thread.sleep(waitTime * 1000 + (long) (Math.random() * 4000));
                    yield true;
                }
                case 3 -> {
                    wait.until(ExpectedConditions.elementToBeClickable(selector));
                    yield true;
                }
                case 4 -> {
                    wait.until(ExpectedConditions.visibilityOfElementLocated(selector));
                    yield true;
                }
                case 5 -> {
                    wait.until(ExpectedConditions.presenceOfElementLocated(selector));
                    yield true;
                }
                case 6 -> wait.until(ExpectedConditions.textToBePresentInElementLocated(selector, elText));
                case 7 -> wait.until(ExpectedConditions.numberOfWindowsToBe(windowsCountNum));
                default -> {
                    JavascriptExecutor js = (JavascriptExecutor) driver;
                    yield js.executeScript("return document.readyState").equals("complete");
                }
            };
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return isOk;
    }
}
